﻿/* 
 *  <copyright file="TexturedQuadCmp.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SobrietyEngine.Actor;

namespace SobrietyEngine.Components.Graphic
{
    public class TexturedQuadCmp : Base3DGraphic, SobrietyEngine.Components.IComponent
    {
        protected BasicEffect basicEffect;
        protected Texture2D texture;
        protected String textureName;
        GraphicsDevice graphics;

        VertexPositionNormalTexture[] triangleData;
        int[] vertInd;

        Matrix world;
        
        //our verts when we make the quad
        Vector3 UL = new Vector3(-0.5f,  0.5f, 0);//upper left
        Vector3 LL = new Vector3(-0.5f, -0.5f, 0);//lower left
        Vector3 UR = new Vector3( 0.5f,  0.5f, 0);//upper worldRight
        Vector3 LR = new Vector3( 0.5f, -0.5f, 0);//lower worldRight

        //4, one for Normal, HFlip, VFlip, HVFLip
        IndexBuffer[] indBuff = new IndexBuffer[4];
        VertexBuffer[] vertBuff = new VertexBuffer[4];

        int curBuff = 0;
        protected Boolean hFlip = false;
        public Boolean HFlip { get { return hFlip; } set { hFlip = value; SetFlipBuff(); } }
        protected Boolean vFlip = false;
        public Boolean VFlip { get { return vFlip; } set { vFlip = value; SetFlipBuff(); } }
        private void SetFlipBuff()
        {
            curBuff = 0;
            if (hFlip)
                curBuff++;
            if (vFlip)
                curBuff += 2;
        }

        public Boolean Static { get; set; }

        IOrientation Rotation;
        IPosition Position;
        IScale Scale;

        Vector3 scale = new Vector3(1, 1, 1);

        Vector3 rot = Vector3.Zero;
        float size = 1.0f;
        Vector3 pos = new Vector3();
        

        public TexturedQuadCmp()
            : base()
        {
            Static = false;
        }

        public TexturedQuadCmp(Texture2D _tex)
            : base()
        {
            texture = _tex;
            textureName = texture.Name;
        }

        public TexturedQuadCmp(String _textureName)
            :base()
        {
            textureName = _textureName;
        }

        /// <summary>
        /// Initializes a new args of the <see cref="TexturedQuadCmp"/> class.
        /// </summary>
        /// <param name="_textureName">Name of the _texture.</param>
        /// <param name="quadVectors">The quad vectors, in order of LL, UL, LR, UR</param>
        public TexturedQuadCmp(String _textureName, Vector3[] quadVectors)
        {
            textureName = _textureName;
            LL = quadVectors[0];
            UL = quadVectors[1];
            LR = quadVectors[2];
            UR = quadVectors[3];
        }

        override public Boolean InitComponent()
        {

            base.InitComponent();

            graphics = Actor.Game.GraphicsDevice;

            Rotation =  Actor.Get<IOrientation>();

            Position = Actor.Get<IPosition>();
            if (Position == null)
                throw new Exception("Position required");

            Scale = Actor.Get<IScale>();
            if (Scale != null)
                scale = Scale.Scale;

            //if we are set to static, we want to make sure the position, scaleCmp, and Orientation componentshave been
            //initialized already. also check farseer body, since it could potentially control those things
            IFarseerPhysicsBody fpb = Actor.Get<IFarseerPhysicsBody>();
            if (fpb != null)
            {
                IComponent c = fpb as IComponent;
                if (c.Initialized == false)
                    return false; //delay
            }

            //get our texture
            if (texture == null)
            {
                //try to laod a texture
                texture = Actor.GameServices().TextureGameService[textureName];
                if (texture == null)
                    Logging.Logger.GetSingleton().WriteError("TexturedQuadCmp::Initialize()-> null texture");
            }

            //create quads

            //Create a array of Vertex Position Texture
            Vector2[] tNorms = new Vector2[4];

            //normal
            tNorms[0] = new Vector2(0.0f, 1.0f);
            tNorms[1] = new Vector2(0.0f, 0.0f);
            tNorms[2] = new Vector2(1.0f, 1.0f);
            tNorms[3] = new Vector2(1.0f, 0.0f);
            CreateQuads(0, tNorms);
            //hFlip
            tNorms[0] = new Vector2(1.0f, 1.0f);
            tNorms[1] = new Vector2(1.0f, 0.0f);
            tNorms[2] = new Vector2(0.0f, 1.0f);
            tNorms[3] = new Vector2(0.0f, 0.0f);
            CreateQuads(1, tNorms);
            //vFLip
            tNorms[0] = new Vector2(0.0f, 0.0f);//UL
            tNorms[1] = new Vector2(0.0f, 1.0f);//BL
            tNorms[2] = new Vector2(1.0f, 0.0f);//UR
            tNorms[3] = new Vector2(1.0f, 1.0f);//BR
            CreateQuads(2, tNorms);
            //hvFLip
            tNorms[0] = new Vector2(1.0f, 0.0f);
            tNorms[1] = new Vector2(1.0f, 1.0f);
            tNorms[2] = new Vector2(0.0f, 0.0f);
            tNorms[3] = new Vector2(0.0f, 1.0f);
            CreateQuads(2, tNorms);


            //set worldUp our basic effect
            basicEffect = new BasicEffect(graphics);
            basicEffect.EnableDefaultLighting();
            basicEffect.TextureEnabled = true;
            basicEffect.Texture = texture;

            //lets calculate our initial position, Orientation, scaleCmp.
            //and calculate our world matrix. if we are set to static, this is only done once
            //Orientation = roation something

            pos = Position.PositionV3;

            basicEffect.World = Matrix.CreateScale(scale) *
                    Matrix.CreateRotationX(rot.X) *
                    Matrix.CreateRotationY(rot.Y) *
                    Matrix.CreateRotationZ(rot.Z) *
                    Matrix.CreateTranslation(pos);

            world = basicEffect.World;

            return true;
        }

        private void CreateQuads(int quadNum, Vector2[] textNorms)
        {
            //Create a array of Vertex Position Texture
            triangleData = new VertexPositionNormalTexture[4];

            //Set each of the vertex data
            Vector3 norm = new Vector3(0, 0, 1);

            triangleData[0] = new VertexPositionNormalTexture(LL * size, norm, textNorms[0]);
            triangleData[1] = new VertexPositionNormalTexture(UL * size, norm, textNorms[1]);
            triangleData[2] = new VertexPositionNormalTexture(LR * size, norm, textNorms[2]);
            triangleData[3] = new VertexPositionNormalTexture(UR * size, norm, textNorms[3]);

            vertInd = new int[6] { 0, 1, 2, 2, 1, 3 };

            //create our vertex buffer
            vertBuff[quadNum] = new VertexBuffer(graphics, typeof(VertexPositionNormalTexture), triangleData.Length, BufferUsage.WriteOnly);
            vertBuff[quadNum].SetData(triangleData);

            indBuff[quadNum] = new IndexBuffer(graphics, typeof(int), vertInd.Length, BufferUsage.WriteOnly);
            indBuff[quadNum].SetData(vertInd);
        }

        public override void UpdateGraphic(GameTime time)
        {
            if (!Static)
            {
                
                if (Scale != null)
                    scale = Scale.Scale;

                pos = Position.PositionV3;

                world = Matrix.CreateScale(scale) *
                        Matrix.CreateRotationX(rot.X) *
                        Matrix.CreateRotationY(rot.Y) *
                        Matrix.CreateRotationZ(rot.Z) *
                        Matrix.CreateTranslation(pos);
            }
        }


        public override void Draw(ICamera _camera, Matrix _projection, Matrix _view, Matrix _world)
        {
            basicEffect.Projection = _projection;
            basicEffect.View = _view;
            basicEffect.World = world;

            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                graphics.SetVertexBuffer(vertBuff[curBuff]);
                graphics.Indices = indBuff[curBuff];

                int noVertices = vertBuff[curBuff].VertexCount;
                int noTriangles = indBuff[curBuff].IndexCount / 3;
                graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, noVertices, 0, noTriangles);
            }
        }
    }
}